TypeScript के एक्जैक्ट टाइप्स को एक्सप्लोर करें, जो अप्रत्याशित प्रॉपर्टीज़ को रोकते हैं और कोड को मजबूत बनाते हैं। व्यावहारिक अनुप्रयोगों और सर्वोत्तम प्रथाओं को जानें।
TypeScript एक्जैक्ट टाइप्स: मजबूत कोड के लिए सख्त ऑब्जेक्ट शेप मिलान
TypeScript, JavaScript का एक सुपरसेट, वेब डेवलपमेंट की डायनामिक दुनिया में स्टैटिक टाइपिंग लाता है। जबकि TypeScript टाइप सेफ्टी और कोड मेंटेनेंस के मामले में महत्वपूर्ण लाभ प्रदान करता है, इसकी स्ट्रक्चरल टाइपिंग सिस्टम कभी-कभी अप्रत्याशित व्यवहार का कारण बन सकती है। यहीं पर "एक्जैक्ट टाइप्स" की अवधारणा सामने आती है। जबकि TypeScript में "एक्जैक्ट टाइप्स" नामक कोई इन-बिल्ट फ़ीचर नहीं है, हम TypeScript फ़ीचर्स और तकनीकों के संयोजन के माध्यम से समान व्यवहार प्राप्त कर सकते हैं। यह ब्लॉग पोस्ट यह जानने में मदद करेगा कि कोड को और मजबूत बनाने और सामान्य त्रुटियों को रोकने के लिए TypeScript में सख्त ऑब्जेक्ट शेप मिलान कैसे लागू किया जाए।
TypeScript की स्ट्रक्चरल टाइपिंग को समझना
TypeScript स्ट्रक्चरल टाइपिंग (जिसे डक टाइपिंग भी कहा जाता है) का उपयोग करता है, जिसका अर्थ है कि टाइप की अनुकूलता उनके घोषित नामों के बजाय, टाइप्स के सदस्यों द्वारा निर्धारित की जाती है। यदि किसी ऑब्जेक्ट में किसी टाइप के लिए आवश्यक सभी प्रॉपर्टीज़ हैं, तो उसे अतिरिक्त प्रॉपर्टीज़ होने के बावजूद, उस टाइप के अनुकूल माना जाता है।
उदाहरण के लिए:
interface Point {
x: number;
y: number;
}
const myPoint = { x: 10, y: 20, z: 30 };
function printPoint(point: Point) {
console.log(`X: ${point.x}, Y: ${point.y}`);
}
printPoint(myPoint); // यह ठीक काम करता है, भले ही myPoint में 'z' प्रॉपर्टी हो
इस परिदृश्य में, TypeScript `myPoint` को `printPoint` में पास करने की अनुमति देता है क्योंकि इसमें आवश्यक `x` और `y` प्रॉपर्टीज़ हैं, भले ही इसमें एक अतिरिक्त `z` प्रॉपर्टी हो। जबकि यह लचीलापन सुविधाजनक हो सकता है, यह अनजाने में अतिरिक्त प्रॉपर्टीज़ वाले ऑब्जेक्ट पास करने पर सूक्ष्म बग्स को भी जन्म दे सकता है।
अतिरिक्त प्रॉपर्टीज़ की समस्या
स्ट्रक्चरल टाइपिंग की शिथिलता कभी-कभी त्रुटियों को छुपा सकती है। एक फ़ंक्शन पर विचार करें जो एक कॉन्फ़िगरेशन ऑब्जेक्ट की अपेक्षा करता है:
interface Config {
apiUrl: string;
timeout: number;
}
function setup(config: Config) {
console.log(`API URL: ${config.apiUrl}`);
console.log(`Timeout: ${config.timeout}`);
}
const myConfig = { apiUrl: "https://api.example.com", timeout: 5000, typo: true };
setup(myConfig); // TypeScript यहाँ शिकायत नहीं करता है!
console.log(myConfig.typo); //true प्रिंट करता है। अतिरिक्त प्रॉपर्टी चुपचाप मौजूद है
इस उदाहरण में, `myConfig` में एक अतिरिक्त प्रॉपर्टी `typo` है। TypeScript कोई त्रुटि नहीं देता है क्योंकि `myConfig` अभी भी `Config` इंटरफ़ेस को संतुष्ट करता है। हालाँकि, टाइपो कभी पकड़ा नहीं जाता है, और यदि टाइपो `typoo` के रूप में अभिप्रेत था तो एप्लिकेशन अपेक्षा के अनुसार व्यवहार नहीं कर सकता है। ये दिखने में महत्वहीन मुद्दे जटिल अनुप्रयोगों को डीबग करते समय बड़ी सिरदर्दी बन सकते हैं। एक गुम या गलत वर्तनी वाली प्रॉपर्टी विशेष रूप से कठिन हो सकती है जब ऑब्जेक्ट्स को नेस्टेड ऑब्जेक्ट्स के भीतर से निपटा जा रहा हो।
TypeScript में एक्जैक्ट टाइप्स लागू करने के तरीके
जबकि TypeScript में वास्तविक "एक्जैक्ट टाइप्स" सीधे उपलब्ध नहीं हैं, यहाँ समान परिणाम प्राप्त करने और सख्त ऑब्जेक्ट शेप मिलान लागू करने के कई तरीके दिए गए हैं:
1. `Omit` के साथ टाइप असर्शन का उपयोग करना
`Omit` यूटिलिटी टाइप आपको मौजूदा टाइप से कुछ प्रॉपर्टीज़ को छोड़कर एक नया टाइप बनाने की अनुमति देता है। टाइप असर्शन के साथ मिलकर, यह अतिरिक्त प्रॉपर्टीज़ को रोकने में मदद कर सकता है।
interface Point {
x: number;
y: number;
}
const myPoint = { x: 10, y: 20, z: 30 };
// एक ऐसा टाइप बनाएँ जिसमें केवल Point की प्रॉपर्टीज़ शामिल हों
const exactPoint: Point = myPoint as Omit & Point;
// त्रुटि: टाइप '{ x: number; y: number; z: number; }' को टाइप 'Point' में असाइन नहीं किया जा सकता है।
// ऑब्जेक्ट लिटरल केवल ज्ञात प्रॉपर्टीज़ निर्दिष्ट कर सकता है, और 'z' टाइप 'Point' में मौजूद नहीं है।
function printPoint(point: Point) {
console.log(`X: ${point.x}, Y: ${point.y}`);
}
//फिक्स
const myPointCorrect = { x: 10, y: 20 };
const exactPointCorrect: Point = myPointCorrect as Omit & Point;
printPoint(exactPointCorrect);
यह दृष्टिकोण तब त्रुटि फेंकता है जब `myPoint` में ऐसी प्रॉपर्टीज़ हों जो `Point` इंटरफ़ेस में परिभाषित नहीं हैं।
स्पष्टीकरण: `Omit
2. ऑब्जेक्ट बनाने के लिए फ़ंक्शन का उपयोग करना
आप एक फ़ैक्टरी फ़ंक्शन बना सकते हैं जो इंटरफ़ेस में परिभाषित प्रॉपर्टीज़ को ही स्वीकार करता है। यह दृष्टिकोण ऑब्जेक्ट निर्माण के समय मजबूत टाइप चेकिंग प्रदान करता है।
interface Config {
apiUrl: string;
timeout: number;
}
function createConfig(config: Config): Config {
return {
apiUrl: config.apiUrl,
timeout: config.timeout,
};
}
const myConfig = createConfig({ apiUrl: "https://api.example.com", timeout: 5000 });
//यह संकलित नहीं होगा:
//const myConfigError = createConfig({ apiUrl: "https://api.example.com", timeout: 5000, typo: true });
//आर्गुमेंट टाइप '{ apiUrl: string; timeout: number; typo: true; }' पैरामीटर टाइप 'Config' को असाइन करने योग्य नहीं है।
// ऑब्जेक्ट लिटरल केवल ज्ञात प्रॉपर्टीज़ निर्दिष्ट कर सकता है, और 'typo' टाइप 'Config' में मौजूद नहीं है।
`Config` इंटरफ़ेस में परिभाषित प्रॉपर्टीज़ के साथ निर्मित ऑब्जेक्ट लौटाकर, आप सुनिश्चित करते हैं कि कोई भी अतिरिक्त प्रॉपर्टी अंदर न घुस सके। यह कॉन्फ़िगरेशन बनाना सुरक्षित बनाता है।
3. टाइप गार्ड्स का उपयोग करना
टाइप गार्ड ऐसे फ़ंक्शन होते हैं जो किसी विशिष्ट स्कोप के भीतर किसी वेरिएबल के टाइप को संकीर्ण करते हैं। जबकि वे सीधे अतिरिक्त प्रॉपर्टीज़ को नहीं रोकते हैं, वे आपको उन्हें स्पष्ट रूप से जांचने और उचित कार्रवाई करने में मदद कर सकते हैं।
interface User {
id: number;
name: string;
}
function isUser(obj: any): obj is User {
return (
typeof obj === 'object' &&
obj !== null &&
'id' in obj && typeof obj.id === 'number' &&
'name' in obj && typeof obj.name === 'string' &&
Object.keys(obj).length === 2 // प्रॉपर्टीज़ की संख्या की जाँच करें। नोट: यह नाजुक है और User की सटीक कुंजी गणना पर निर्भर करता है।
);
}
const potentialUser1 = { id: 123, name: "Alice" };
const potentialUser2 = { id: 456, name: "Bob", extra: true };
if (isUser(potentialUser1)) {
console.log("वैध उपयोगकर्ता:", potentialUser1.name);
} else {
console.log("अवैध उपयोगकर्ता");
}
if (isUser(potentialUser2)) {
console.log("वैध उपयोगकर्ता:", potentialUser2.name); // यहाँ हिट नहीं होगा
} else {
console.log("अवैध उपयोगकर्ता");
}
इस उदाहरण में, `isUser` टाइप गार्ड न केवल आवश्यक प्रॉपर्टीज़ की उपस्थिति के लिए जांच करता है, बल्कि उनके प्रकारों और प्रॉपर्टीज़ की *सटीक* संख्या के लिए भी। यह दृष्टिकोण अधिक स्पष्ट है और आपको अमान्य ऑब्जेक्ट्स को सुरुचिपूर्ण ढंग से संभालने की अनुमति देता है। हालाँकि, प्रॉपर्टीज़ की संख्या की जाँच नाजुक है। जब भी `User` प्रॉपर्टीज़ प्राप्त करता/खोता है, तो जाँच को अपडेट किया जाना चाहिए।
4. `Readonly` और `as const` का लाभ उठाना
जबकि `Readonly` मौजूदा प्रॉपर्टीज़ के संशोधन को रोकता है, और `as const` एक रीड-ओनली टपल या ऑब्जेक्ट बनाता है जहाँ सभी प्रॉपर्टीज़ गहराई से रीड-ओनली होती हैं और लिटरल टाइप रखती हैं, उन्हें अन्य तरीकों के साथ जोड़कर एक सख्त परिभाषा और टाइप चेकिंग बनाने के लिए इस्तेमाल किया जा सकता है। हालाँकि, कोई भी अकेले अतिरिक्त प्रॉपर्टीज़ को नहीं रोकता है।
interface Options {
width: number;
height: number;
}
//Readonly टाइप बनाएँ
type ReadonlyOptions = Readonly;
const options: ReadonlyOptions = { width: 100, height: 200 };
//options.width = 300; //त्रुटि: 'width' प्रॉपर्टी केवल-पठनीय होने के कारण असाइन नहीं की जा सकती।
// as const का उपयोग करना
const config = { api_url: "https://example.com", timeout: 3000 } as const;
//config.timeout = 5000; //त्रुटि: 'timeout' प्रॉपर्टी केवल-पठनीय होने के कारण असाइन नहीं की जा सकती।
// हालाँकि, अतिरिक्त प्रॉपर्टीज़ की अभी भी अनुमति है:
const invalidOptions: ReadonlyOptions = { width: 100, height: 200, depth: 300 }; //कोई त्रुटि नहीं। अभी भी अतिरिक्त प्रॉपर्टीज़ की अनुमति है।
interface StrictOptions {
readonly width: number;
readonly height: number;
}
//यह अब त्रुटि देगा:
//const invalidStrictOptions: StrictOptions = { width: 100, height: 200, depth: 300 };
//टाइप '{ width: number; height: number; depth: number; }' को टाइप 'StrictOptions' को असाइन नहीं किया जा सकता है।
// ऑब्जेक्ट लिटरल केवल ज्ञात प्रॉपर्टीज़ निर्दिष्ट कर सकता है, और 'depth' टाइप 'StrictOptions' में मौजूद नहीं है।
यह अपरिवर्तनीयता में सुधार करता है, लेकिन केवल उत्परिवर्तन को रोकता है, अतिरिक्त प्रॉपर्टीज़ के अस्तित्व को नहीं। `Omit`, या फ़ंक्शन दृष्टिकोण के साथ संयुक्त होने पर, यह अधिक प्रभावी हो जाता है।
5. लाइब्रेरीज़ (जैसे Zod, io-ts) का उपयोग करना
Zod और io-ts जैसी लाइब्रेरीज़ शक्तिशाली रनटाइम टाइप सत्यापन और स्कीमा परिभाषा क्षमताएं प्रदान करती हैं। ये लाइब्रेरीज़ आपको ऐसी स्कीमा परिभाषित करने की अनुमति देती हैं जो आपके डेटा के अपेक्षित आकार का सटीक वर्णन करती हैं, जिसमें अतिरिक्त प्रॉपर्टीज़ को रोकना भी शामिल है। जबकि वे रनटाइम निर्भरता जोड़ते हैं, वे एक बहुत ही मजबूत और लचीला समाधान प्रदान करते हैं।
Zod के साथ उदाहरण:
import { z } from 'zod';
const UserSchema = z.object({
id: z.number(),
name: z.string(),
});
type User = z.infer;
const validUser = { id: 1, name: "John" };
const invalidUser = { id: 2, name: "Jane", extra: true };
const parsedValidUser = UserSchema.parse(validUser);
console.log("पार्स किया गया वैध उपयोगकर्ता:", parsedValidUser);
try {
const parsedInvalidUser = UserSchema.parse(invalidUser);
console.log("पार्स किया गया अमान्य उपयोगकर्ता:", parsedInvalidUser); // यह नहीं पहुँचेगा
} catch (error) {
console.error("सत्यापन त्रुटि:", error.errors);
}
Zod का `parse` मेथड इनपुट स्कीमा के अनुरूप न होने पर त्रुटि फेंकेगा, जो प्रभावी रूप से अतिरिक्त प्रॉपर्टीज़ को रोकता है। यह रनटाइम सत्यापन प्रदान करता है और स्कीमा से TypeScript टाइप्स भी उत्पन्न करता है, जो आपके टाइप परिभाषाओं और रनटाइम सत्यापन तर्क के बीच स्थिरता सुनिश्चित करता है।
एक्जैक्ट टाइप्स लागू करने के लिए सर्वोत्तम प्रथाएँ
TypeScript में सख्त ऑब्जेक्ट शेप मिलान को लागू करते समय विचार करने योग्य कुछ सर्वोत्तम प्रथाएँ यहाँ दी गई हैं:
- सही तकनीक चुनें: सबसे अच्छा तरीका आपकी विशिष्ट आवश्यकताओं और परियोजना की आवश्यकताओं पर निर्भर करता है। सरल मामलों के लिए, `Omit` या फ़ैक्टरी फ़ंक्शंस के साथ टाइप असर्शन पर्याप्त हो सकते हैं। अधिक जटिल परिदृश्यों के लिए या जब रनटाइम सत्यापन की आवश्यकता होती है, तो Zod या io-ts जैसी लाइब्रेरीज़ का उपयोग करने पर विचार करें।
- संगत रहें: टाइप सुरक्षा के एक समान स्तर को बनाए रखने के लिए अपनी चुनी हुई तकनीक को अपने कोडबेस में लगातार लागू करें।
- अपने टाइप्स का दस्तावेज़ीकरण करें: अपने इंटरफेस और टाइप्स को स्पष्ट रूप से दस्तावेज़ित करें ताकि अन्य डेवलपर्स को अपने डेटा के अपेक्षित आकार का संचार किया जा सके।
- अपने कोड का परीक्षण करें: अपने टाइप बाधाओं के अपेक्षा के अनुसार काम करने और आपके कोड अमान्य डेटा को सुरुचिपूर्ण ढंग से संभालने के लिए यूनिट परीक्षण लिखें।
- समझौतों पर विचार करें: सख्त ऑब्जेक्ट शेप मिलान लागू करने से आपका कोड अधिक मजबूत हो सकता है, लेकिन यह विकास समय को भी बढ़ा सकता है। लाभों को लागतों के विरुद्ध तौलें और वह दृष्टिकोण चुनें जो आपकी परियोजना के लिए सबसे अधिक समझ में आता है।
- क्रमिक अपनाना: यदि आप एक बड़े मौजूदा कोडबेस पर काम कर रहे हैं, तो इन तकनीकों को धीरे-धीरे अपनाने पर विचार करें, अपने एप्लिकेशन के सबसे महत्वपूर्ण भागों से शुरुआत करें।
- ऑब्जेक्ट आकृतियों को परिभाषित करते समय इंटरफेस को टाइप उपनामों पर प्राथमिकता दें: इंटरफ़ेस आम तौर पर पसंद किए जाते हैं क्योंकि वे घोषणा मर्जिंग का समर्थन करते हैं, जो विभिन्न फ़ाइलों में टाइप्स का विस्तार करने के लिए उपयोगी हो सकता है।
वास्तविक दुनिया के उदाहरण
आइए कुछ वास्तविक दुनिया के परिदृश्यों पर एक नज़र डालें जहाँ एक्जैक्ट टाइप्स फायदेमंद हो सकते हैं:
- API अनुरोध पेलोड: API को डेटा भेजते समय, यह सुनिश्चित करना महत्वपूर्ण है कि पेलोड अपेक्षित स्कीमा के अनुरूप हो। एक्जैक्ट टाइप्स को लागू करने से अप्रत्याशित प्रॉपर्टीज़ भेजने से होने वाली त्रुटियों को रोका जा सकता है। उदाहरण के लिए, कई भुगतान प्रसंस्करण API अत्यंत संवेदनशील डेटा के लिए होते हैं।
- कॉन्फ़िगरेशन फ़ाइलें: कॉन्फ़िगरेशन फ़ाइलों में अक्सर बड़ी संख्या में प्रॉपर्टीज़ होती हैं, और टाइपो आम हो सकते हैं। एक्जैक्ट टाइप्स का उपयोग करके इन टाइपो को जल्दी पकड़ा जा सकता है। यदि आप क्लाउड डिप्लॉयमेंट में सर्वर स्थान सेट कर रहे हैं, तो स्थान सेटिंग में टाइपो (जैसे, eu-west-1 बनाम eu-wet-1) को शुरू में पकड़े जाने पर डीबग करना अत्यंत कठिन हो जाएगा।
- डेटा ट्रांसफ़ॉर्मेशन पाइपलाइन: डेटा को एक प्रारूप से दूसरे प्रारूप में बदलते समय, यह सुनिश्चित करना महत्वपूर्ण है कि आउटपुट डेटा अपेक्षित स्कीमा के अनुरूप हो।
- मैसेज क्यू: मैसेज क्यू के माध्यम से संदेश भेजते समय, यह सुनिश्चित करना महत्वपूर्ण है कि मैसेज पेलोड मान्य हो और उसमें सही प्रॉपर्टीज़ हों।
उदाहरण: अंतर्राष्ट्रीयकरण (i18n) कॉन्फ़िगरेशन
कल्पना कीजिए कि एक बहुभाषी एप्लिकेशन के लिए अनुवाद का प्रबंधन कर रहे हैं। आप इस तरह एक कॉन्फ़िगरेशन ऑब्जेक्ट बना सकते हैं:
interface Translation {
greeting: string;
farewell: string;
}
interface I18nConfig {
locale: string;
translations: Translation;
}
const englishConfig: I18nConfig = {
locale: "en-US",
translations: {
greeting: "Hello",
farewell: "Goodbye"
}
};
//यह एक समस्या होगी, क्योंकि एक अतिरिक्त प्रॉपर्टी मौजूद है, जो चुपचाप एक बग पेश करती है।
const spanishConfig: I18nConfig = {
locale: "es-ES",
translations: {
greeting: "Hola",
farewell: "Adiós",
typo: "unintentional translation"
}
};
//समाधान: Omit का उपयोग करना
const spanishConfigCorrect: I18nConfig = {
locale: "es-ES",
translations: {
greeting: "Hola",
farewell: "Adiós"
} as Omit & Translation
};
एक्जैक्ट टाइप्स के बिना, एक अनुवाद कुंजी में एक टाइपो (जैसे `typo` फ़ील्ड जोड़ना) बिना ध्यान दिए रह सकता है, जिससे उपयोगकर्ता इंटरफ़ेस में अनुवाद गायब हो सकते हैं। सख्त ऑब्जेक्ट शेप मिलान को लागू करके, आप इन त्रुटियों को विकास के दौरान पकड़ सकते हैं और उन्हें उत्पादन तक पहुँचने से रोक सकते हैं।
निष्कर्ष
जबकि TypeScript में अंतर्निहित "एक्जैक्ट टाइप्स" नहीं हैं, आप TypeScript फ़ीचर्स और `Omit` के साथ टाइप असर्शन, फ़ैक्टरी फ़ंक्शन, टाइप गार्ड्स, `Readonly`, `as const`, और Zod और io-ts जैसी बाहरी लाइब्रेरीज़ जैसी तकनीकों के संयोजन का उपयोग करके समान परिणाम प्राप्त कर सकते हैं। सख्त ऑब्जेक्ट शेप मिलान को लागू करके, आप अपने कोड की मजबूती में सुधार कर सकते हैं, सामान्य त्रुटियों को रोक सकते हैं, और अपने अनुप्रयोगों को अधिक विश्वसनीय बना सकते हैं। याद रखें कि उस दृष्टिकोण को चुनें जो आपकी आवश्यकताओं के अनुरूप हो और इसे अपने कोडबेस में लगातार लागू करें। इन दृष्टिकोणों पर सावधानीपूर्वक विचार करके, आप अपने एप्लिकेशन के टाइप्स पर अधिक नियंत्रण ले सकते हैं और दीर्घकालिक रखरखाव क्षमता बढ़ा सकते हैं।